package impl

import (
	"context"
	"fmt"
	"os"

	"gitee.com/go-course/go8/projects/devcloud/mcenter/apps/code"
	"gitee.com/go-course/go8/projects/devcloud/mcenter/apps/notify"
	"gitee.com/go-course/go8/projects/devcloud/mcenter/apps/token/provider"
	"github.com/infraboard/mcube/exception"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
)

// 验证码的颁发
func (s *service) IssueCode(ctx context.Context, req *code.IssueCodeRequest) (
	*code.IssueCodeResponse, error) {
	// 获取验证码颁发器
	// 颁发的方式可能很多(Userpassword/LDAP/Dingdingautho/AccessToken)
	issuer := provider.Get(req.IssueBy)

	// 确保有provider
	if issuer == nil {
		return nil, exception.NewBadRequest("grant type %s not support", req.IssueBy)
	}

	// 颁发验证码
	c, err := issuer.IssueCode(ctx, req)
	if err != nil {
		return nil, err
	}

	// 保存
	if _, err := s.col.InsertOne(ctx, c); err != nil {
		return nil, exception.NewInternalServerError("inserted verify code(%s) document error, %s",
			c, err)
	}

	// 发送验证码
	msg, err := s.send(ctx, c)
	if err != nil {
		return nil, exception.NewInternalServerError("send verify code error, %s", err)
	}

	return &code.IssueCodeResponse{Message: msg}, nil
}

// 如何把验证码通知给用户
func (s *service) send(ctx context.Context, c *code.Code) (string, error) {
	req := &notify.SendSMSRequest{
		TemplateId:     os.Getenv("SMS_TENCENT_TEMPLATE_ID"),
		TemplateParams: []string{c.Code, fmt.Sprintf("%d", c.ExpiredMinite)},
		// 用户名称, 提前确保 admin用户配置了手机号码
		Users: []string{c.Username},
	}

	resp, err := s.notify.SendSMS(ctx, req)
	if err != nil {
		return "", err
	}

	message := fmt.Sprintf("验证码已通过短信发送到你的手机: %v, 请及时查收", resp.SuccessedNumbers)
	return message, nil
}

func (s *service) VerifyCode(ctx context.Context, req *code.VerifyCodeRequest) (
	*code.Code, error) {
	if err := req.Validate(); err != nil {
		return nil, exception.NewBadRequest("validate check code request error, %s", err)
	}

	code := &code.Code{}
	if err := s.col.FindOne(ctx, bson.M{"_id": req.HashID()}).Decode(code); err != nil {
		if err == mongo.ErrNoDocuments {
			return nil, exception.NewNotFound("verify code: %s  not found", req.Code)
		}
		return nil, exception.NewInternalServerError("find system config %s error, %s", req.Code, err)
	}

	// 校验Token是否过期
	if code.IsExpired() {
		return nil, exception.NewPermissionDeny("verify code is expired")
	}

	// 没过去验证成功, 删除
	if err := s.delete(ctx, code); err != nil {
		s.log.Errorf("delete check ok verify code error, %s", err)
	}

	return code, nil
}
